home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1998 July / EnigmA AMIGA RUN 29 (1998)(G.R. Edizioni)(IT)[!][issue 1998-07 & 08].iso / earkit / news / thor / rexx / sortmail.br < prev    next >
Text File  |  1998-05-24  |  31KB  |  935 lines

  1. /*
  2. ** $VER: SortMail.br 3.43 (14.9.97)
  3. ** by Eirik Nicolai Synnes
  4. **
  5. ** Some code borrowed from AddSOUP.thor     by Magne Østlyngen
  6. **                     and AddAmiNetList.br by Petter Nilsen
  7. **
  8. ** See SortMail.guide for documentation
  9. **
  10. */
  11.  
  12.  
  13. options results
  14. options failat 31
  15.  
  16. signal on syntax
  17. signal on break_c
  18. signal on halt
  19.  
  20. parse arg arguments
  21.  
  22.  
  23. /*
  24. ** Initialize some variables
  25. */
  26.  
  27. version         = subword(sourceline(2), 4, 1)
  28. cfgfile         = 'SortMail.cfg'
  29.  
  30. multisel        = 0
  31. trigskipped     = 0
  32.  
  33. globals = 'sigl thorport progwin thorpath msgs. cfgfile trigcount killcount localcount owncount sucount data. head. text. addrs. globalcfg. trigger. bbsdata. conflist. cursys. BBSREAD.LASTERROR THOR.LASTERROR multisel forcetrig trigskipped globals'
  34.  
  35.  
  36. /*
  37. ** See if I'm run from Thor
  38. */
  39.  
  40. if (left(address(), 5) = 'THOR.') then thorport = address()
  41. else do
  42.     say 'As of the Thor 2.5 release SortMail can only be run from Thor.'
  43.    say 'Please read SortMail.guide for more information.'
  44.     exit(0)
  45. end
  46.  
  47.  
  48. /*
  49. ** Find Thor's path and open BBSREAD ARexx port
  50. */
  51.  
  52. if ~(open(tp, 'ENV:THOR/THORPATH', 'R')) then displayerror(30, 'SortMail', 'Couldn''t find Thor''s path.')
  53. thorpath = readln(tp)
  54. close(tp)
  55.  
  56. if ~(show('P', 'BBSREAD')) then do
  57.     address(command)
  58.     'Run >NIL: `GetEnv THOR/THORPath`bin/LoadBBSRead'
  59.     'WaitForPort BBSREAD'
  60.     if (rc ~= 0) then displayerror(30, 'SortMail', 'Couldn''t open BBSREAD''s ARexx port.')
  61. end
  62.  
  63. /*
  64. ** See if another copy of SortMail is already running
  65. */
  66.  
  67. if (getclip('SM_Active') ~= '') then call notify('Another copy of SortMail is probably running.\nDo you want to continue?', 'Yes|No')
  68. if (result = 0) then exit(0)
  69.  
  70. call setclip('SM_Active', 'True')
  71.  
  72.  
  73. /*
  74. ** Determine how SortMail was started
  75. */
  76.  
  77. address(thorport)
  78. 'CURRENTSYSTEM STEM 'cursys
  79. if (rc = 1) then call displayerror(20, 'SortMail', 'SortMail has to be run from inside a system.')
  80. if (rc > 1) then call displayerror(30, 'SortMail', 'CURRENTSYSTEM: 'THOR.LASTERROR)
  81.  
  82. if (cursys.CONFNAME = '') then do
  83.     if (getclip('SM_Notified') = '') then do
  84.         call notify('As of Thor 2.5 mail is automatically sorted at the\ntime of import. This makes it unnecessary to run\nSortMail.br when entering a system.\n\nPlease remove the SortMail.br entry in this system''s\nconfiguration. You can find it in System\nConfiguration -> Files/Paths -> Entering System.\n\nSortMail.guide has more information.', 'Ok')
  85.         call setclip('SM_Notified', 'Done')
  86.     end
  87.     signal cleanup
  88. end
  89.  
  90.  
  91. /*
  92. ** Display some progress info
  93. */
  94.  
  95. address(thorport)
  96. 'OPENPROGRESS TITLE "SortMail.br 'version'" PT "Initializing..." AT "_Abort" PCW 40'
  97. if (rc ~= 0) then call displayerror(30, 'SortMail', 'OPENPROGRESS: 'THOR.LASTERROR)
  98. progwin = result
  99.  
  100.  
  101. /*
  102. ** Utilize BBSRead's copyback buffer
  103. */
  104.  
  105. address(bbsread)
  106. 'BUFMODE COPYBACK'
  107.  
  108.  
  109. /*
  110. ** Get system information
  111. */
  112.  
  113.  
  114. address(bbsread)
  115. 'GETBBSDATA "'cursys.BBSNAME'" 'bbsdata
  116. if (rc ~= 0) then call displayerror(30, 'SortMail', 'GETBBSDATA, 'globalcfg.SYSTEM': 'BBSREAD.LASTERROR)
  117. if (right(bbsdata.BBSPATH, 1) ~= ':') & (right(bbsdata.BBSPATH, 1) ~= '/') then bbsdata.BBSPATH = bbsdata.BBSPATH || '/'
  118.  
  119.  
  120. /*
  121. ** Read configuration and set ARexx clips
  122. */
  123.  
  124. call readcfg()
  125.  
  126.  
  127. /*
  128. ** Process messages
  129. */
  130.  
  131. call procmsgs()
  132.  
  133.  
  134. /*
  135. ** Beat it
  136. */
  137.  
  138. signal cleanup
  139.  
  140.  
  141. /*
  142. ** Some error detection stuff
  143. */
  144.  
  145. error:
  146. syntax:
  147.  
  148. select
  149.     when (symbol('BBSREAD.LASTERROR') = 'VAR') then displayerror(rc, 'SortMail', 'Line 'sigl' in SortMail.br: 'BBSREAD.LASTERROR)
  150.     when (symbol('THOR.LASTERROR')    = 'VAR') then displayerror(rc, 'SortMail', 'Line 'sigl' in SortMail.br: 'THOR.LASTERROR)
  151.     otherwise displayerror(rc, 'SortMail', 'Line 'sigl' in SortMail.br returned 'rc': 'errortext(rc))
  152. end
  153.  
  154. break_c:
  155. halt:
  156. cleanup:
  157.  
  158.  
  159. /*
  160. ** Turn off copyback buffer
  161. */
  162.  
  163. address(bbsread)
  164. 'BUFMODE ENDCOPYBACK'
  165.  
  166.  
  167. /*
  168. ** Close progressbar if open
  169. */
  170.  
  171. if (symbol('progwin') = 'VAR') & (progwin ~= 0)  then do
  172.     address(thorport)
  173.     'CLOSEPROGRESS REQ 'progwin
  174.     progwin = 0
  175.  
  176.     if (symbol('trigcount') = 'VAR') & (trigcount > 0) then do
  177.         'CURRENTSYSTEM STEM 'cursys
  178.         if (rc > 1) then call displayerror(30, 'SortMail', 'CURRENTSYSTEM: 'THOR.LASTERROR)
  179.         if (upper(cursys.CONFNAME) = upper(globalcfg.CONFERENCE)) then 'SHOWCONFERENCE "'globalcfg.CONFERENCE'"'
  180.         'UPDATECONFWINDOW'
  181.     end
  182. end
  183.  
  184.  
  185. /*
  186. ** Remove ARexx clip and temporary files
  187. */
  188.  
  189. call setclip('SM_Active')
  190.  
  191. if (exists('T:SortMail.result')) then address command 'Delete T:SortMail.result QUIET'
  192.  
  193.  
  194. /*
  195. ** Have a jolly nice day
  196. */
  197.  
  198. exit(0)
  199.  
  200.  
  201.  /****************************************************************************
  202. *********************** Get list of messages to process ***********************
  203.  ****************************************************************************/
  204.  
  205. getmsgarray: interpret 'procedure expose 'globals
  206.  
  207.  
  208. /*
  209. ** Get conference data for incoming conference
  210. */
  211.  
  212. address(bbsread)
  213. 'GETCONFDATA "'globalcfg.SYSTEM'" "'globalcfg.CONFERENCE'" STEM 'confdata
  214. if (rc ~= 0) then call displayerror(30, 'SortMail', 'GETCONFDATA: 'BBSREAD.LASTERROR)
  215.  
  216. /*
  217. ** Build message stem
  218. */
  219.  
  220. address(thorport)
  221. 'GETMSGLISTSELECTED STEM 'msglist
  222. if (rc = 0) then do
  223.     if (confdata.NAME ~= cursys.CONFNAME) then displayerror(30, 'SortMail', 'You can only multiselect messages for\nprocessing in the Email conference\nconfigured in CfgSortMail.thor.')
  224.     do i = 1 to msglist.count; msgs.i = msglist.i; end; msgs.count = msglist.count
  225.     multisel = 1
  226. end
  227. else do
  228.     address(thorport)
  229.     'GETMESSAGEARRAY SYSTEM "'globalcfg.SYSTEM'" CONF "'globalcfg.CONFERENCE'" STEM 'msgs
  230.     if (rc = 5) then signal cleanup
  231.     else if (rc ~= 0) then call displayerror(30, 'SortMail', 'GETMESSAGEARRAY: 'THOR.LASTERROR)
  232. end
  233.  
  234. /*
  235. ** Exit if there are no messages to process
  236. */
  237.  
  238. if (msgs.count = 0) then signal cleanup
  239.  
  240. return(0)
  241.  
  242.  
  243.  /****************************************************************************
  244. ************************** Process messages on system *************************
  245.  ****************************************************************************/
  246.  
  247. procmsgs: interpret 'procedure expose 'globals
  248.  
  249. trigcount = 0; failcount = 0; killcount = 0; localcount = 0; owncount = 0; sucount = 0
  250.  
  251. BDB_ADD_USERS           =  9  /* Parser should add users to database. */
  252.  
  253. MDB_DELETED             =  5  /* Message is deleted. */
  254. MDB_MARKED              = 10  /* Message is marked. */
  255. MDB_SUPERMARKED         = 13  /* Message will not be unmarked as long as this flag is set. */
  256.  
  257. UDB_DELETED             =  0  /* User is deleted */
  258.  
  259. if (multisel) then do
  260.     call notify('You have multi selected ' || msgs.count || ' messages.\n\nDo you want to force these messages\nto be processed by one particular trigger\nor should they be processed normally?', 'Normal|_Force|Abort')
  261.     if (result = 0) then signal cleanup
  262.     else if (result = 2) then do
  263.         do i = 1 to trigger.count; triglist.i = trigger.i.name; end
  264.         triglist.count = trigger.count
  265.         address(thorport)
  266.         'REQUESTLIST INSTEM 'triglist' TITLE "Select a trigger" SIZEGADGET'
  267.         select
  268.             when (rc = 0) then forcetrig = result
  269.             when (rc = 5) then signal cleanup
  270.             otherwise call displayerror(rc, 'SortMail', 'REQUESTLIST: 'THOR.LASTERROR)
  271.         end
  272.     end
  273. end
  274.  
  275. do i = 1 to msgs.count
  276.     failed = 0; dodelmsg = 0; dodeluser = 0; islocal = 1; gothit = 0
  277.     drop data. head. text.
  278.  
  279.     /* Update progressreport */
  280.     progtext = globalcfg.SYSTEM': 'i' of 'msgs.count' (#'msgs.i')'
  281.     address(thorport)
  282.     'UPDATEPROGRESS REQ 'progwin' TOTAL 'msgs.count' CURRENT 'i' PT "'progtext'"'
  283.     if (rc = 5) then break
  284.     else if (rc > 0) then call displayerror(rc, 'SortMail', 'UPDATEPROGRESS: 'THOR.LASTERROR, msgs.i)
  285.  
  286.  
  287.     /*
  288.     ** Read message data stem
  289.     */
  290.  
  291.     address(bbsread)
  292.     'READBRMESSAGE "'globalcfg.SYSTEM'" "'globalcfg.CONFERENCE'" 'msgs.i' DATASTEM 'data
  293.     if (rc ~= 0) then do
  294.         call displayerror(10, 'SortMail', 'READBRMESSAGE: 'BBSREAD.LASTERROR, msgs.i)
  295.         iterate i
  296.     end
  297.  
  298.  
  299.     /*
  300.     ** If messsage is deleted or superunread then skip it
  301.     */
  302.  
  303.     if (bittst(data.FLAGS, MDB_DELETED)) then iterate i
  304.     if ~(multisel) & (bittst(data.FLAGS, MDB_SUPERMARKED)) then do
  305.         sucount = sucount + 1; iterate i
  306.     end
  307.  
  308.  
  309.     /*
  310.     ** Read the rest of the message
  311.     */
  312.  
  313.     'READBRMESSAGE "'globalcfg.SYSTEM'" "'globalcfg.conference'" 'msgs.i' HEADSTEM 'head' TEXTSTEM 'text
  314.     if (rc ~= 0) then do
  315.         call displayerror(10, 'SortMail', 'READBRMESSAGE: 'BBSREAD.LASTERROR, msgs.i)
  316.         iterate i
  317.     end
  318.  
  319.  
  320.     /*
  321.     ** Check if it is a local message or not
  322.     */
  323.  
  324.     if (symbol('text.COMMENT.COUNT') = 'VAR') & (text.COMMENT.COUNT > 0) then do c = 1 to text.COMMENT.COUNT
  325.         if (upper(subword(text.COMMENT.c, 1, 1))) = 'RECEIVED:' then do
  326.             islocal = 0; leave c
  327.         end
  328.     end
  329.  
  330.  
  331.     /*
  332.     ** Trigger loop
  333.     */
  334.  
  335.     address(bbsread)
  336.     do j = 1 to trigger.count
  337.         if trigger.j.skip then iterate j
  338.  
  339.         foundmsg = 0; foundcrits = 0
  340.  
  341.         if (multisel) then if (forcetrig = trigger.j.name) then foundmsg = 1
  342.  
  343.         /*
  344.         ** Search in names, addresses and subject
  345.         */
  346.  
  347.         do k = 1 to trigger.j.search.count while foundmsg = 0
  348.             select
  349.                 when (trigger.j.search.k.type = 'FROMADDR') then do
  350.                     if (trigger.j.search.k.not) then do
  351.                         if ~(index(upper(head.FROMADDR), upper(trigger.j.search.k.criteria)) > 0) then foundmsg = 1
  352.                     end
  353.                     else do
  354.                         if (index(upper(head.FROMADDR), upper(trigger.j.search.k.criteria)) > 0) then foundmsg = 1
  355.                     end
  356.                 end
  357.                 when (trigger.j.search.k.type = 'FROMNAME') then do
  358.                     if (trigger.j.search.k.not) then do
  359.                         if ~(index(upper(head.FROMNAME),  upper(trigger.j.search.k.criteria)) > 0) then foundmsg = 1
  360.                     end
  361.                     else do
  362.                         if (index(upper(head.FROMNAME), upper(trigger.j.search.k.criteria)) > 0) then foundmsg = 1
  363.                     end
  364.                 end
  365.                 when (trigger.j.search.k.type = 'TOADDR') | (trigger.j.search.k.type = 'TONAME') then do
  366.                     call parseaddr(0)
  367.                     if ~(trigger.j.search.k.not) then do
  368.                         do l = 1 to addrs.COUNT while foundmsg = 0
  369.                             if (trigger.j.search.k.type = 'TOADDR') & (addrs.l.addr ~= '') & (index(upper(addrs.l.ADDR), upper(trigger.j.search.k.criteria)) > 0) then foundmsg = 1
  370.                             if (trigger.j.search.k.type = 'TONAME') & (addrs.l.name ~= '') & (index(upper(addrs.l.NAME), upper(trigger.j.search.k.criteria)) > 0) then foundmsg = 1
  371.                         end
  372.                     end
  373.                     else do
  374.                         foundcrit = 0
  375.                         do l = 1 to addrs.COUNT while foundcrit = 0
  376.                             if (trigger.j.search.k.type = 'TOADDR') & (addrs.l.addr ~= '') & (index(upper(addrs.l.ADDR), upper(trigger.j.search.k.criteria)) > 0) then foundcrit = 1
  377.                             if (trigger.j.search.k.type = 'TONAME') & (addrs.l.name ~= '') & (index(upper(addrs.l.NAME), upper(trigger.j.search.k.criteria)) > 0) then foundcrit = 1
  378.                         end
  379.                         if ~(foundcrit) then foundmsg = 1
  380.                         drop foundcrit
  381.                     end
  382.                 end
  383.                 when (trigger.j.search.k.type = 'SUBJECT') then do
  384.                     if trigger.j.search.k.not then do
  385.                         if ~(index(upper(head.SUBJECT), upper(trigger.j.search.k.criteria)) > 0) then foundmsg = 1
  386.                     end
  387.                     else do
  388.                         if (index(upper(head.SUBJECT), upper(trigger.j.search.k.criteria)) > 0) then foundmsg = 1
  389.                     end
  390.                 end
  391.                 when (trigger.j.search.k.type = 'REPLYTO') then do
  392.                     if trigger.j.search.k.not then do
  393.                         if ~(index(upper(text.REPLYADDR), upper(trigger.j.search.k.criteria)) > 0) then foundmsg = 1
  394.                     end
  395.                     else do
  396.                         if (index(upper(text.REPLYADDR), upper(trigger.j.search.k.criteria)) > 0) then foundmsg = 1
  397.                     end
  398.                 end
  399.                 otherwise do
  400.                     if (trigger.j.search.k.type ~= 'HEADER') & (trigger.j.search.k.type ~= 'BODY') then do
  401.                         if (symbol('trigger.j.search.k.type') = 'VAR') then call displayerror(5, 'SortMail', 'Unsupported SEARCH type in trigger 'j': ' || trigger.j.search.k.type, msgs.i)
  402.                         else call displayerror(5, 'SortMail', 'Trigger contains invalid search entry', msgs.i)
  403.                     end
  404.                 end
  405.             end
  406.             if (trigger.j.matchall) & (foundmsg) then do; foundcrits = foundcrits + 1; foundmsg = 0; end
  407.         end
  408.  
  409.         /*
  410.         ** If no match was made, search header fields or message body
  411.         */
  412.  
  413.         do k = 1 to trigger.j.search.count while foundmsg = 0
  414.             if (trigger.j.search.k.type = 'HEADER') then do
  415.                 foundcrit = 0
  416.                 if (symbol('text.COMMENT.COUNT') = 'VAR') & (text.COMMENT.COUNT > 0) then do l = 1 to text.COMMENT.COUNT while foundmsg = 0
  417.                     if (upper(left(text.COMMENT.l, length(trigger.j.search.k.keyword))) = upper(trigger.j.search.k.keyword)) then do
  418.                         if (trigger.j.search.k.not) then do
  419.                             if (index(upper(subword(text.COMMENT.l, 2)), upper(trigger.j.search.k.criteria)) > 0) then foundcrit = 1
  420.                         end
  421.                         else do
  422.                             if (index(upper(subword(text.COMMENT.l, 2)), upper(trigger.j.search.k.criteria)) > 0) then foundmsg = 1
  423.                         end
  424.                     end
  425.                 end
  426.                 if (trigger.j.search.k.not) & ~(foundcrit) then foundmsg = 1
  427.                 drop foundcrit
  428.             end
  429.  
  430.             if (trigger.j.search.k.type = 'BODY') then do
  431.                 foundcrit = 0
  432.                 if (symbol('text.TEXT.COUNT') = 'VAR') & (text.TEXT.COUNT > 0) then do l = 1 to text.TEXT.COUNT while foundmsg = 0
  433.                     if (trigger.j.search.k.not) then do
  434.                         if (index(upper(text.TEXT.l), upper(trigger.j.search.k.criteria)) > 0) then foundcrit = 1
  435.                     end
  436.                     else do
  437.                         if (index(upper(text.TEXT.l), upper(trigger.j.search.k.criteria)) > 0) then foundmsg = 1
  438.                     end
  439.                 end
  440.                 if (trigger.j.search.k.not) & ~(foundcrit) then foundmsg = 1
  441.                 drop foundcrit
  442.             end
  443.  
  444.             if (trigger.j.matchall) & (foundmsg) then do; foundcrits = foundcrits + 1; foundmsg = 0; end
  445.         end
  446.  
  447.         if (trigger.j.matchall) & (foundcrits = trigger.j.search.count) then foundmsg = 1
  448.  
  449.         /*
  450.         ** If no match was made, skip to next trigger
  451.         */
  452.  
  453.         if (foundmsg = 0) then iterate j
  454.  
  455.         /*
  456.         ** Delete message if local messages are not wanted
  457.         */
  458.  
  459.         if (islocal) & (trigger.j.NOLOCAL) then do
  460.             address(bbsread)
  461.             'UPDATEBRMESSAGE "'globalcfg.system'" "'globalcfg.CONFERENCE'" 'msgs.i' SETDELETED'
  462.             if (islocal) then localcount = localcount + 1
  463.             iterate i
  464.         end
  465.  
  466.         trigcount = trigcount + 1; gothit = 1
  467.  
  468.         /*
  469.         ** Execute internal actions
  470.         */
  471.  
  472.         do k = 1 to trigger.j.action.count while failed = 0
  473.  
  474.             returned = 0
  475.  
  476.             select
  477.                 when (trigger.j.action.k.type = 'COPY') then do
  478.                     opts = ''
  479.                     if (symbol('trigger.j.action.k.destsys') = 'VAR') then opts = opts || 'DESTSYS "'trigger.j.action.k.destsys'" '
  480.                     if (symbol('trigger.j.action.k.replyaddr') = 'VAR') then opts = opts || 'REPLYADDR "'trigger.j.action.k.replyaddr'" '
  481.                     call runexternal(thorpath || 'rexx/bbsread/CopyMessage.br', 'SYSTEM %s CONFERENCE "'globalcfg.CONFERENCE'" MSGNO 'msgs.i' DESTCONF "'trigger.j.action.k.destconf'" 'opts)
  482.                     returned = result
  483.                 end
  484.  
  485.                 when (trigger.j.action.k.type = 'RECENT') then do
  486.                     call runexternal(thorpath || 'rexx/bbsread/ParseRECENT.br', 'SYSTEM "'globalcfg.system'" CONFERENCE "'globalcfg.CONFERENCE'" MSGNO 'msgs.i' 'trigger.j.action.k.args)
  487.                     returned = result
  488.                 end
  489.  
  490.                 when (trigger.j.action.k.type = 'SAVEMESSAGE') then do
  491.                     call runexternal(thorpath || 'rexx/bbsread/SaveMessage.br', 'SYSTEM "'globalcfg.SYSTEM'" CONFERENCE "'globalcfg.CONFERENCE'" MSGNO 'msgs.i' 'trigger.j.action.k.args)
  492.                     returned = result
  493.                 end
  494.  
  495.                 when (trigger.j.action.k.type = 'SPLITDIGEST') then do
  496.                if (index(upper(trigger.j.action.k.args), 'DESTSYS') = 0) then trigger.j.action.k.args = trigger.j.action.k.args || ' DESTSYS "'globalcfg.SYSTEM'"'
  497.                     call runexternal(thorpath || 'rexx/bbsread/SplitDigest.br', 'SYSTEM "'globalcfg.SYSTEM'" CONFERENCE "'globalcfg.CONFERENCE'" MSGNO 'msgs.i' 'trigger.j.action.k.args)
  498.                     returned = result
  499.                 end
  500.  
  501.                 otherwise if (trigger.j.action.k.type ~= 'EXTERNAL') then do
  502.                     returned = 5
  503.                     if (symbol('trigger.j.action.type') = 'VAR') then call displayerror(returned, 'SortMail', 'Unsupported ACTION type: 'trigger.j.action.type, msgs.i)
  504.                     else call displayerror(returned, 'SortMail', 'Trigger contains invalid action entry', msgs.i)
  505.                 end
  506.             end
  507.  
  508.             if (returned ~= 0) then do
  509.                 failcount = failcount + 1; failed = 1
  510.             end
  511.             else do
  512.                 if (trigger.j.delmsg = 1)  then dodelmsg = 1
  513.                 if (trigger.j.deluser = 1) then dodeluser = 1
  514.             end
  515.         end
  516.  
  517.         /*
  518.         ** Execute external actions
  519.         */
  520.  
  521.         do k = 1 to trigger.j.action.count while failed = 0
  522.  
  523.             returned = 0
  524.  
  525.             if (trigger.j.action.k.type = 'EXTERNAL') then do
  526.                 call runexternal(trigger.j.action.k.scriptname, trigger.j.action.k.scriptopts, msgs.i)
  527.                 returned = result
  528.                 if (returned ~= 0) then do
  529.                     failcount = failcount + 1; failed = 1
  530.                 end
  531.                 else do
  532.                     if (trigger.j.delmsg = 1)  then dodelmsg = 1
  533.                     if (trigger.j.deluser = 1) then dodeluser = 1
  534.                 end
  535.             end
  536.         end
  537.  
  538.         leave
  539.     end
  540.  
  541.     /*
  542.     ** If no actions were configured then check if message should be
  543.    ** deleted
  544.     */
  545.  
  546.     if (trigger.j.action.count = 0) & (trigger.j.delmsg = 1) then dodelmsg = 1
  547.  
  548.     /*
  549.     ** Move to local messages to sent mail conference if no hit was made
  550.     */
  551.  
  552.     if ~(gothit) & (islocal) & (symbol('globalcfg.LOCALTO') = 'VAR') & (upper(head.FROMADDR) = upper(bbsdata.EMAILADDR)) then do
  553.         call runexternal(thorpath || 'rexx/bbsread/CopyMessage.br', 'SYSTEM %s CONFERENCE "'globalcfg.CONFERENCE'" MSGNO 'msgs.i' DESTCONF "'globalcfg.LOCALTO'"')
  554.         if (result ~= 0) then dodelmsg = 0
  555.         else do
  556.             dodelmsg = 1
  557.             owncount = owncount + 1
  558.         end
  559.     end
  560.  
  561.     if (failed ~= 0) then iterate i
  562.  
  563.  
  564.     /*
  565.     ** Delete user?
  566.     */
  567.  
  568.     address(bbsread)
  569.     if (dodeluser = 1) & (bittst(bbsdata.FLAGS, BDB_ADD_USERS)) then do
  570.         drop suser.
  571.         'SEARCHBRUSER BBSNAME "'globalcfg.SYSTEM'" STEM 'suser' SEARCH "'head.FROMADDR'" ADDRESS'
  572.         if (rc ~= 0) then call displayerror(10, 'SortMail', 'SEARCHBRUSER: 'BBSREAD.LASTERROR, msgs.i)
  573.         if (result > 0) then do n = 1 to suser.COUNT
  574.             if (suser.n.FOUNDINTAG = 1) then do
  575.                 drop duser. tuser.
  576.                 'READBRUSER BBSNAME "'globalcfg.SYSTEM'" USERNR 'suser.n.USERNR' DATASTEM 'duser' TAGSSTEM 'tuser
  577.                 if (rc ~= 0) then call displayerror(10, 'SortMail', 'READBRUSER: 'BBSREAD.LASTERROR, msgs.i)
  578.                 if ~(bittst(duser.FLAGS, UDB_DELETED)) & (data.MSGDATE < duser.USERDATE + 2) & (data.MSGDATE > duser.USERDATE - 2) & (head.FROMNAME = tuser.NAME) then do
  579.                     'WRITEBRUSER BBSNAME "'globalcfg.SYSTEM'" UPDATEUSERNR 'suser.n.USERNR' DELETEUSER'
  580.                     if (rc ~= 0) then call displayerror(30, 'SortMail', 'WRITEBRUSER: 'BBSREAD.LASTERROR, msgs.i)
  581.                 end
  582.             end
  583.         end
  584.     end
  585.     drop suser. tuser. duser.
  586.  
  587.  
  588.     /*
  589.     ** Delete message?
  590.     */
  591.  
  592.     address(bbsread)
  593.     if (dodelmsg = 1) then do
  594.         'UPDATEBRMESSAGE "'globalcfg.SYSTEM'" "'globalcfg.conference'" 'msgs.i' SETDELETED'
  595.         if (rc ~= 0) then displayerror(30, 'SortMail', 'UPDATEBRMESSAGE: 'BBSREAD.LASTERROR, msgs.i)
  596.     end
  597. end
  598.  
  599. message = 'A total of ' || msgs.count || ' messages were scanned.\n'
  600.  
  601. if trigcount  = 0 then message = message || 'No messages were processed by a trigger.\n'
  602. if trigcount  > 0 then message = message || trigcount  || ' of these were processed by a trigger.\n'
  603. if sucount    > 0 then message = message || sucount    || ' messages were marked as superunread.\n'
  604. if killcount  > 0 then message = message || killcount  || ' message copy actions were caught by a kill entry.\n'
  605. if localcount > 0 then message = message || localcount || ' messages were local copies that were removed.\n'
  606. if owncount   > 0 then message = message || owncount   || ' messages were moved to "' || globalcfg.LOCALTO || '".\n'
  607.  
  608. if trigskipped = 1 then message = message || '\nNB! One or more triggers used search methods\nnot available in this ARexx script and\nwere skipped. See SortMail.guide.\n\n'
  609.  
  610. if failcount  > 0 then message = message || failcount || ' messages returned an error.'
  611. else message = message || 'No errors occured.'
  612.  
  613. call notify(message, 'Ok')
  614.  
  615. return(0)
  616.  
  617.  
  618.  /****************************************************************************
  619. *************** Put addresses and names in a string into a stem ***************
  620.  ****************************************************************************/
  621.  
  622. parseaddr: interpret 'procedure expose 'globals
  623.            parse arg checkcc
  624.  
  625. i = 1; acnt = 0; usedhead = 0; drop addrs.
  626.  
  627. if (symbol('head.TOADDR') = 'VAR') & ~(index(head.TOADDR, ',') > 0) then do
  628.     acnt = acnt + 1; addrs.acnt.name = ''; addrs.acnt.cc = 0; usedhead = 1
  629.     addrs.acnt.addr = head.TOADDR
  630.     if (symbol('head.TONAME') = 'VAR') then addrs.acnt.name = head.TONAME
  631. end
  632.  
  633. if (symbol('text.COMMENT.COUNT') = 'VAR') then if (text.COMMENT.COUNT > 0) then do while i <= text.COMMENT.COUNT
  634.     thiscc = 0
  635.  
  636.     if (checkcc = 1) & (upper(subword(text.COMMENT.i, 1, 1)) = 'CC:') then thiscc = 1
  637.  
  638.     if (thiscc) | (upper(subword(text.COMMENT.i, 1, 1)) = 'TO:') then do
  639.         addrs = subword(text.COMMENT.i, 2)
  640.         do forever
  641.             addrs = strip(addrs, 'B', ' ' || '09'x)
  642.  
  643.             offset = 1
  644.             do forever
  645.                 length = index(substr(addrs, offset), ','); if (length = 0) then length = length(addrs) - offset + 1
  646.                 thisaddr = strip(substr(addrs, offset, length), 'B', ', ');
  647.                 acnt = acnt + 1; addrs.acnt.addr = ''; addrs.acnt.name = ''
  648.                 if (thiscc) then addrs.acnt.cc = 1; else addrs.acnt.cc = 0
  649.  
  650.                 if (words(thisaddr) = 1) then addrs.acnt.addr = strip(thisaddr, 'B', '<>()')
  651.                 else if (index(thisaddr, '<') > 0) then do
  652.                     addrstart  = index(thisaddr, '<')
  653.                     addrlength = index(substr(thisaddr, addrstart), '>')
  654.                     addrs.acnt.addr = strip(substr(thisaddr, addrstart + 1, addrlength), 'B', '> ')
  655.                     addrs.acnt.name = strip(delstr(thisaddr, addrstart, addrlength), 'B', ' "' || '27'x)
  656.                 end
  657.                 else do j = 1 to words(thisaddr)
  658.                     thispart = strip(subword(thisaddr, j, 1), 'B', '<>" ' || '27'x)
  659.                     if (index(thispart, '@') > 0) then addrs.acnt.addr = thispart
  660.                     else addrs.acnt.name = addrs.acnt.name || thispart || ' '
  661.                 end
  662.  
  663.                 if ~(thiscc) & (usedhead) & (addrs.acnt.addr = addrs.1.addr) & (addrs.acnt.name = addrs.1.name) then do
  664.                     drop addrs.acnt.; acnt = acnt - 1
  665.                 end
  666.  
  667.                 if (offset + length >= length(addrs)) then break
  668.                 offset = offset + length
  669.             end
  670.  
  671.             j = i + 1; if ~((c2d(left(text.COMMENT.j, 1)) = 9) | (c2d(left(text.COMMENT.j, 1)) = 32)) then break
  672.             i = i + 1; addrs = text.COMMENT.i
  673.         end
  674.     end
  675.     i = i + 1
  676. end
  677.  
  678. addrs.COUNT = acnt
  679.  
  680. return(0)
  681.  
  682.  
  683.  /****************************************************************************
  684. **************************** Run an external script ***************************
  685.  ****************************************************************************/
  686.  
  687. runexternal: interpret 'procedure expose 'globals
  688.              parse arg scriptname, scriptopts, msgno
  689.  
  690. /*
  691. ** Replace arguments
  692. */
  693.  
  694. scriptopts = substitute(scriptopts, "%s", '"'globalcfg.SYSTEM'"')
  695.  
  696. scriptopts = substitute(scriptopts, "%c", '"'globalcfg.CONFERENCE'"')
  697.  
  698. scriptopts = substitute(scriptopts, "%n", msgno)
  699.  
  700. /*
  701. ** Make sure the path is valid
  702. */
  703.  
  704. if (index(scriptname, ':') > 0) then scriptpath = scriptname
  705. else scriptpath = thorpath || scriptname
  706.  
  707. /*
  708. ** Run the script
  709. */
  710.  
  711. if ~(exists(scriptpath)) then displayerror(20, 'SortMail', 'Could not find external script "'scriptpath'"', msgno)
  712. else do
  713.     address(command)
  714.     'rx >T:SortMail.result 'scriptpath' 'scriptopts
  715.     returned = rc
  716.  
  717.     /*
  718.     ** Check for returned errors
  719.     */
  720.  
  721.     if (returned > 0) then do
  722.         resopen = open(rf, 'T:SortMail.result', 'R')
  723.         if (resopen) then do
  724.             res = readln(rf)
  725.             if (left(res, 20) = 'rx failed returncode') then do
  726.                 res2 = readln(rf)
  727.                 if (res2 ~= '') then res = res2
  728.             end
  729.             call close(rf)
  730.         end
  731.         else res = 'Unknown error'
  732.  
  733.         call displayerror(returned, 'External script 'scriptpath, res, msgno)
  734.     end
  735. end
  736.  
  737. if (exists('T:SortMail.result')) then 'Delete T:SortMail.result QUIET'
  738.  
  739. return(returned)
  740.  
  741.  
  742.  /****************************************************************************
  743. *********************** Open and read configuration file **********************
  744.  ****************************************************************************/
  745.  
  746. readcfg: interpret 'procedure expose 'globals
  747.          parse arg procsys
  748.  
  749. if ~(exists(bbsdata.BBSPATH || cfgfile)) then call displayerror(30, 'SortMail', 'Couldn''t find configuration file ('bbsdata.BBSPATH || cfgfile').')
  750.  
  751. triggers = 0
  752.  
  753. address(bbsread)
  754.  
  755. cfgopen = open(cf, bbsdata.BBSPATH || cfgfile, 'R')
  756.  
  757. if (cfgopen) then do
  758.     cfglength = seek(cf, 0, 'E'); call seek(cf, 0, 'B'); cfgline = 0
  759.  
  760.     address(thorport)
  761.     'UPDATEPROGRESS REQ 'progwin' TOTAL 'cfglength' CURRENT 0 PT "'cursys.BBSNAME': Reading configuration..."'
  762.     if (rc = 5) then signal cleanup
  763.     else if (rc > 0) then call displayerror(rc, 'SortMail', 'UPDATEPROGRESS: 'THOR.LASTERROR, msgs.i)
  764.  
  765.     do until (seek(cf, 0) = cfglength)
  766.         entry = readln(cf); cfgline = cfgline + 1
  767.  
  768.         if (symbol('progwin') = 'VAR') then do
  769.             address(thorport)
  770.             'UPDATEPROGRESS 'progwin' CURRENT 'seek(cf, 0)
  771.             if (rc = 5) then signal cleanup
  772.             if (rc ~= 0) then displayerror(rc, 'Read config', 'UPDATEPROGRESS: 'THOR.LASTERROR)
  773.         end
  774.  
  775.         address(bbsread)
  776.         select
  777.             when (upper(subword(entry, 1, 1)) = "SYSTEM") then displayerror(30, 'Read config', 'Found old 2.x configuration file. SortMail has\nchanged the configuration file format in version\n3.0. Please create a new one with CfgSortMail.thor.')
  778.  
  779.             when (upper(subword(entry, 1, 1)) = "GLOBAL") then do
  780.                 'READARGS TEMPLATE "SYSTEM/K,CONFERENCE/A,STATISTICS/S,NOWARN/S,LOGINSTATE/S,LOCALTO/K" STEM 'globalcfg' CMDLINE 'subword(entry, 2)
  781.                 if (rc ~= 0) then call displayerror(30, 'Read config', 'Error in 'cfgfile' line 'cfgline': 'BBSREAD.LASTERROR)
  782.                 globalcfg.SYSTEM = cursys.BBSNAME
  783.  
  784.                 /* See if there are any messages to process */
  785.                 call getmsgarray()
  786.             end
  787.  
  788.             when (upper(subword(entry, 1, 1)) = "TRIGGER") then do
  789.                 triggers = triggers + 1
  790.                 trigger.triggers.cmdline = subword(entry, 2)
  791.  
  792.                 trigger.triggers.DELMSG = 0; trigger.triggers.DELUSER = 0; trigger.triggers.MATCHALL = 0; trigger.triggers.NOLOCAL = 0
  793.                 'READARGS TEMPLATE "NAME/A,DELMSG/S,DELUSER/S,MATCHALL/S,NOLOCAL/S,GROUPS/K" STEM 'trigger.triggers' CMDLINE 'trigger.triggers.cmdline
  794.                 if (rc ~= 0) then call displayerror(30, 'Read config', 'Error in 'cfgfile' line 'cfgline': 'BBSREAD.LASTERROR)
  795.                 if (symbol('trigger.triggers.groups') = 'VAR') then if (trigger.triggers.groups ~= 'EMail') then trigger.triggers.skip = 1; else trigger.triggers.skip = 0
  796.  
  797.                 do
  798.                     searches = 0; actions = 0
  799.  
  800.                     do until (upper(subentry) = 'ENDTRIGGER') | (eof(cf))
  801.                         subentry = readln(cf); cfgline = cfgline + 1
  802.                         select
  803.                             when (upper(subword(subentry, 1, 1)) = 'ACTION') then do
  804.                                 actions = actions + 1
  805.                                 trigger.triggers.action.actions.type = upper(subword(subentry, 2, 1))
  806.  
  807.                                 if (trigger.triggers.action.actions.type = 'COPY') | (trigger.triggers.action.actions.type = 'EXTERNAL') then do
  808.                                     'READARGS TEMPLATE "DESTSYS/K,DESTCONF/K,REPLYADDR/K,SCRIPTNAME/K,SCRIPTOPTS/K" STEM 'trigger.triggers.action.actions' CMDLINE 'subword(subentry, 3)
  809.                                     if (rc ~= 0) then call displayerror(30, 'Read config', 'Error in 'cfgfile' line 'cfgline': 'BBSREAD.LASTERROR)
  810.                                 end
  811.                                 else trigger.triggers.action.actions.args = subword(subentry, 3)
  812.  
  813.                                 if (upper(trigger.triggers.action.actions.type) = 'MAIL') | (upper(trigger.triggers.action.actions.type) = 'FORWARD') then trigger.triggers.skip = 1; else trigger.triggers.skip = 0
  814.                             end
  815.  
  816.                             when (upper(subword(subentry, 1, 1)) = 'SEARCH') then do
  817.                                 searches = searches + 1
  818.                                 trigger.triggers.search.searches.type = upper(subword(subentry, 2, 1)); trigger.triggers.search.searches.not = 0
  819.                                 'READARGS TEMPLATE "CRITERIA/K,SUBSTR/K,KEYWORD/K,PATTERN/K,NOT/S" STEM 'trigger.triggers.search.searches' CMDLINE 'subword(subentry, 3)
  820.                                 if (rc ~= 0) then call displayerror(30, 'Read config', 'Error in 'cfgfile' line 'cfgline': 'BBSREAD.LASTERROR)
  821.                                 if (symbol('trigger.triggers.search.searches.substr') = 'VAR') then do; trigger.triggers.search.searches.criteria = trigger.triggers.search.searches.substr; drop trigger.triggers.search.searches.substr; end
  822.                                 if ((symbol('trigger.triggers.search.searches.criteria') ~= 'VAR') & (symbol('trigger.triggers.search.searches.pattern') ~= 'VAR')) | ((symbol('trigger.triggers.search.searches.criteria') = 'VAR') & (symbol('trigger.triggers.search.searches.pattern') = 'VAR')) then call displayerror(20, 'SortMail', 'Error in 'cfgfile' line 'cfgline':\nIllegal use of search parameters.')
  823.                                 if (symbol('trigger.triggers.search.searches.pattern') = 'VAR') then trigger.triggers.skip = 1; else trigger.triggers.skip = 0
  824.                             end
  825.  
  826.                             when (eof(cf)) then call displayerror(30, 'Read config', 'Premature end of configuration file 'cfgfile)
  827.  
  828.                             when (upper(subword(subentry, 1, 1)) = 'TRIGGER') then call displayerror(30, 'Read config', 'TRIGGER did not end with ENDTRIGGER in 'cfgfile' line 'cfgline)
  829.  
  830.                             otherwise nop
  831.                         end
  832.                     end
  833.                 end
  834.  
  835.                 trigger.triggers.action.count = actions
  836.                 trigger.triggers.search.count = searches
  837.                 if trigger.triggers.skip = 1 then trigskipped = 1
  838.             end
  839.  
  840.             otherwise nop
  841.         end
  842.  
  843.         trigger.count = triggers
  844.     end
  845. end
  846. else do
  847.     call displayerror(30, 'Read config', 'Couldn''t open ' || bbsdata.BBSPATH || cfgfile || ' for reading.')
  848. end
  849.  
  850. if (trigger.count = 0) then signal cleanup
  851.  
  852. call close(cf)
  853.  
  854. return(0)
  855.  
  856.  
  857.  /****************************************************************************
  858. *********************** Display requester / type message **********************
  859.  ****************************************************************************/
  860.  
  861. notify: interpret 'procedure expose 'globals
  862.         parse arg message, choices
  863.  
  864. seperator = index(choices, '|')
  865. if (seperator > 0) then do
  866.     firstchoice = substr(choices, 1, seperator - 1)
  867.     secondchoice = substr(choices, seperator + 1)
  868. end
  869.  
  870. address(bbsread)
  871. 'BUFMODE ENDCOPYBACK'
  872.  
  873. message = substitute(message, '"', '*"')
  874.  
  875. address(thorport)
  876. 'REQUESTNOTIFY "'message'" "'choices'"'
  877. reqres = result
  878.  
  879. address(bbsread)
  880. 'BUFMODE COPYBACK'
  881.  
  882. return(reqres)
  883.  
  884.  
  885.  /****************************************************************************
  886. *********************** Display error and return or quit **********************
  887.  ****************************************************************************/
  888.  
  889. displayerror: interpret 'procedure expose 'globals
  890.               parse arg returned, caller, errmsg, msgno
  891.  
  892. if (msgno ~= '') then errhead = caller' returned 'returned' on message #'msgno':'
  893. else errhead = caller' returned 'returned' in line 'sigl':'
  894.  
  895. select
  896.     when (returned > 0) & (returned < 20) then do
  897.         call notify(errhead || '\n' || errmsg, 'Continue|Abort')
  898.         if (result = 0) then signal cleanup
  899.     end
  900.  
  901.     when (returned > 19) then do
  902.         call notify(errhead || '\n' || errmsg, 'Abort')
  903.         signal cleanup
  904.     end
  905.  
  906.     otherwise nop
  907. end
  908.  
  909. drop THOR.LASTERROR BBSREAD.LASTERROR
  910.  
  911. return(0)
  912.  
  913.  
  914.  /****************************************************************************
  915. ********************* Substitute a string within a string *********************
  916. ******** Shamelessly ripped from Troels Walsted Hansen's UUDecode.thor ********
  917.  ****************************************************************************/
  918.  
  919. substitute: interpret 'procedure expose 'globals
  920.             parse arg str, org, new
  921.  
  922. lastfound = 0
  923.  
  924. found = index(str, org)
  925.  
  926. do while found > lastfound
  927.     secondpart = substr(str, found + length(org))
  928.     firstpart = substr(str, 1, length(str) - length(substr(str, found)))
  929.     str = firstpart || new || secondpart
  930.     lastfound = found + length(new)
  931.     found = index(str, org, lastfound)
  932. end
  933.  
  934. return(str)
  935.